﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace WordsReminder
{
    class DataEncryption
    {
        private byte[] pKey;
        private static readonly byte[] iv;
        SymmetricAlgorithm provider;
        private const int _keySize = 32;
        private const int _bufferSize = 2048;

        static DataEncryption()
        {
            iv = new byte[_keySize/2];
        }

        public DataEncryption(string privateKey)
        {
            pKey = StringToByteArray(privateKey);
            provider = SymmetricAlgorithm.Create();
            provider.Key = pKey;
            provider.IV = iv;
        }

        public Stream GetEncryptedStream(Stream inStream)
        {
            // Make sure we got valid input
            if (inStream == null) throw new ArgumentNullException("Invalid stream.", "inStream");

            // Create the output stream
            MemoryStream outStream = new MemoryStream();
            CryptoStream encryptStream = new CryptoStream(outStream, provider.CreateEncryptor(), CryptoStreamMode.Write);

            // Encrypt the stream by reading all bytes from the input stream and
            // writing them to the output encryption stream.  Note that we're depending
            // on the fact that ~CryptoStream does not close the underlying stream.
            int numBytes;
            byte[] inputBytes = new byte[_bufferSize];
            while ((numBytes = inStream.Read(inputBytes, 0, inputBytes.Length)) != 0)
            {
                encryptStream.Write(inputBytes, 0, numBytes);
            }
            encryptStream.FlushFinalBlock();

            // Go back to the beginning of the newly encrypted stream and return it
            outStream.Position = 0;
            return outStream;
        }

        public Stream GetDecryptedStream(Stream inStream)
        {
            // Make sure we got valid input
            if (inStream == null) throw new ArgumentNullException("Invalid stream.", "inStream");

            // Create the input and output streams
            CryptoStream decryptStream = new CryptoStream(inStream, provider.CreateDecryptor(), CryptoStreamMode.Read);
            MemoryStream outStream = new MemoryStream();

            // Read the stream and write it to the output. Note that we're depending
            // on the fact that ~CryptoStream does not close the underlying stream.
            int numBytes;
            byte[] inputBytes = new byte[_bufferSize];
            while ((numBytes = decryptStream.Read(inputBytes, 0, inputBytes.Length)) != 0)
            {
                outStream.Write(inputBytes, 0, numBytes);
            }

            // Go to the beginning of the decrypted stream and return it
            outStream.Position = 0;
            return outStream;
        }

        public string GetEncryptedString(string inString)
        {
            using (Stream inStream=new MemoryStream(Encoding.Default.GetBytes(inString)))
            {
                using (Stream outStream = GetEncryptedStream(inStream))
                {
                    byte[] resBytes = new byte[outStream.Length];
                    outStream.Read(resBytes, 0, resBytes.Length);
                    return ByteArrayToString(resBytes);
                }
            }
        }

        public string GetDecryptedString(string inString)
        {
            using (Stream inStream = new MemoryStream(StringToByteArray(inString)))
            {
                using (Stream outStream = GetDecryptedStream(inStream))
                {
                    byte[] resBytes = new byte[outStream.Length];
                    outStream.Read(resBytes, 0, resBytes.Length);
                    return Encoding.Default.GetString(resBytes);
                }
            }
        }

        public static byte[] StringToByteArray(String str)
        {
            //int NumberChars = str.Length;
            //byte[] bytes = new byte[NumberChars / 2];
            //for (int i = 0; i < NumberChars; i += 2)
            //    bytes[i / 2] = Convert.ToByte(str.Substring(i, 2), 16);
            //return bytes;
            return Convert.FromBase64String(str);
        }

        public static string ByteArrayToString(byte[] ba)
        {
            //string hex = BitConverter.ToString(ba);
            //return hex.Replace("-", "");
            return Convert.ToBase64String(ba);
        }


        public static string GetEncryptedString(string inString, string privateKey)
        {
            DataEncryption temp = new DataEncryption(privateKey);
            return temp.GetEncryptedString(inString);
        }

        public static string GetDecryptedString(string inString, string privateKey)
        {
            DataEncryption temp = new DataEncryption(privateKey);
            return temp.GetDecryptedString(inString);
        }
    }
}
